package com.autumn.context.bean;

import com.autumn.context.annotation.*;
import com.autumn.context.exception.BeanNoUniqueException;
import com.autumn.context.io.PropertyResolver;
import com.autumn.context.io.ResourceResolver;
import com.autumn.context.utils.ClassUtils;
import org.jetbrains.annotations.Nullable;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author niann
 * @description
 * @date 2023/12/21 22:08
 */
public class AnnotationConfigApplicationContext {
    Map<String, BeanDefinition> beans;

    public AnnotationConfigApplicationContext(Class<?> clazz, PropertyResolver propertyResolver) {
        // 扫描获取所有Bean的Class类型:
        Set<String> beanClassNames = scanForClassNames(clazz);

        // 创建Bean的定义:
        this.beans = createBeanDefinitions(beanClassNames);
    }

    public AnnotationConfigApplicationContext() {

    }

    public static void main(String[] args) {
        HashMap<String, String> map = new HashMap<>();
        System.out.println(map.put("user", "年年"));
        System.out.println(map.put("user", "年年"));
    }

    private BeanDefinition buildBeanDefinition(String className) {
        return null;
    }


    /**
     * 根据获取的Class类型创建Bean的定义
     *
     * @param beanClassNames
     * @return
     */
    private Map<String, BeanDefinition> createBeanDefinitions(Set<String> beanClassNames) {
        HashMap<String, BeanDefinition> beans = new HashMap<>();
        for (String className : beanClassNames) {
            Class<?> clazz = null;
            try {
                clazz = Class.forName(className);
            } catch (ClassNotFoundException e) {
                throw new RuntimeException(e);
            }
            Component component = ClassUtils.findAnnotation(clazz, Component.class);
            if (component != null) {
                try {
                    String beanName = ClassUtils.getBeanName(clazz);
                    BeanDefinition beanDefinition =
                            new BeanDefinition(beanName, clazz, clazz.getConstructor().newInstance()
                                    , clazz.getConstructor(), null, null,
                                    ClassUtils.getOrder(clazz),
                                    clazz.isAnnotationPresent(Primary.class),
                                    null, null,
                                    ClassUtils.findAnnotationMethod(clazz, Init.class),
                                    ClassUtils.findAnnotationMethod(clazz, PreDestroy.class));
                    addBeanDefinition(beans, beanDefinition);
                    Configuration conf = ClassUtils.findAnnotation(clazz, Configuration.class);
                    if (conf != null) {
                        scanfactotyMethod(beanName, clazz, beans);
                    }
                } catch (NoSuchMethodException | InstantiationException | IllegalAccessException |
                         InvocationTargetException e) {
                    throw new RuntimeException(e);
                }
            }

        }
        return beans;
    }

    private void addBeanDefinition(HashMap<String, BeanDefinition> beans, BeanDefinition beanDefinition) {
        if (beans.put(beanDefinition.name, beanDefinition) != null) {
            throw new RuntimeException("Duplicate bean name: " + beanDefinition.name);
        }
    }


    /**
     * 根据@Bean修饰的方法创建Bean
     *
     * @param clazz
     * @param beans
     */
    private void scanfactotyMethod(String factoryName, Class<?> clazz, HashMap<String, BeanDefinition> beans) {
        try {
            for (Method method : clazz.getDeclaredMethods()) {
                Bean bean = method.getAnnotation(Bean.class);
                if (bean != null) {
                    Class<?> beanClazz = method.getReturnType();
                    var def = new BeanDefinition(method.getName(), beanClazz,
                            null, null,
                            factoryName, method, getOrder(method), method.isAnnotationPresent(Primary.class),
                            bean.initMethod().isEmpty() ? null : bean.initMethod(),
                            bean.destroyMethod().isEmpty() ? null : bean.destroyMethod(), null, null);
                    addBeanDefinition(beans, def);
                }
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取@Order注解的值
     *
     * @param method
     * @return
     */
    private int getOrder(Method method) {
        Order order = method.getAnnotation(Order.class);
        return order != null ? order.value() : 0;
    }


    /**
     * @param clazz 扫描类ComponentScan下的包
     *              ComponentScan为null则默认扫描当前包
     *              没有ComponentScan则抛异常
     * @return
     */
    private Set<String> scanForClassNames(Class<?> clazz) {
        ComponentScan scan = ClassUtils.findAnnotation(clazz, ComponentScan.class);
        //获取ComponentScan配置的包名未配置则默认当前类所在包
        if (scan == null) {
            throw new RuntimeException("无法找到@ComponentScan");
        }
        String[] scanPacks = scan.value();
        if (scanPacks == null || scanPacks.length == 0) {
            scanPacks = new String[]{clazz.getPackageName()};
        }
        Set<String> classNameSet = new HashSet<>();
        //扫描scanPacks
        for (String pack : scanPacks) {
            ResourceResolver rr = new ResourceResolver(pack);
            List<String> clsaaList = rr.scan(res -> {
                String name = res.name();
                if (name.endsWith(".class")) {
                    return res.name().substring(0, name.length() - 6).replace("\\", ".").replace("/", ".");
                }
                return null;
            }).stream().toList();
            classNameSet.addAll(clsaaList);
        }
        Import importAnnotation = ClassUtils.findAnnotation(clazz, Import.class);
        if (importAnnotation != null) {
            for (Class<?> aClass : importAnnotation.value()) {
                String name = aClass.getName();
                classNameSet.add(name);
            }
        }
        return classNameSet;
    }


    @Nullable
    public BeanDefinition findBeandefinition(String beanName) {
        return beans.get(beanName);
    }

    @Nullable
    private List<BeanDefinition> findBeanDefinitionList(Class<?> clazz) {
        return this.beans
                .values()
                .stream()
                .filter(bean -> clazz.isAssignableFrom(bean.getBeanClass()))
                .sorted()
                .collect(Collectors.toList());
    }

    /**
     * 根据类型找到唯一的bean，如果找不到，返回null
     * 如果有多个则根据@Primary返回
     *
     * @param clazz
     * @return
     */
    @Nullable
    private BeanDefinition findBeanDefinition(Class<?> clazz) {
        List<BeanDefinition> beanList = findBeanDefinitionList(clazz);
        if (beanList == null || beanList.isEmpty()) {
            return null;
        }
        if (beanList.size() == 1) {
            return beanList.get(0);
        }
        List<BeanDefinition> primaryBeans = beanList.stream().filter(BeanDefinition::isPrimary).toList();
        if (primaryBeans.size() == 1) {
            return primaryBeans.get(0);
        }
        if (primaryBeans.isEmpty()) {
            throw new BeanNoUniqueException("Bean不是唯一的并且没有设置primary属性: {%s}".formatted(clazz.getName()));
        } else {
            throw new BeanNoUniqueException("无法找到唯一的Bean: {%s}".formatted(clazz.getName()));
        }
    }
}
